package com.ziytek.web.citizen.service;

import com.ziytek.web.citizen.caffeine.CaffeinePut;
import com.ziytek.web.citizen.config.caffeine.CaffeineUtil;
import com.ziytek.web.citizen.constant.NumConstant;
import com.ziytek.web.citizen.constant.ServerConstant;
import com.ziytek.web.citizen.constant.SettingConstant;
import com.ziytek.web.citizen.enums.StepTypeEnum;
import com.ziytek.web.citizen.enums.StepTypeNoEnum;
import com.ziytek.web.citizen.event.CommandFirstDataEvent;
import com.ziytek.web.citizen.exception.IssuedException;
import com.ziytek.web.citizen.mapper.BasicCabinetIpMapper;
import com.ziytek.web.citizen.model.bean.*;
import com.ziytek.web.citizen.pojo.basic.BasicStepSettings;
import com.ziytek.web.citizen.util.*;
import io.netty.buffer.Unpooled;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * @author fenghx
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class CommandExecuteService {

    private final CaffeinePut caffeinePut;
    private final BasicCabinetIpMapper cabinetIpMapper;
    private final CommandFirstDataService commandFirstDataService;

    public boolean execute(CommandIssuedSuccess command, String reqCabinetNo, List<CommandOutInfo> commandOutInfos,
                           AddSettings settings, BasicStepSettings step, Integer expectedTimeSecond) {
        if (null == command || StringUtils.isBlank(reqCabinetNo) || null == settings || null == step) {
            throw new IssuedException("10003", "通道参数错误");
        }

        Assert.notEmpty(commandOutInfos, "commandOutInfos is empty");

        var cabinetNo = CabinetNoUtil.getCabinetNo(reqCabinetNo);
        var ips = cabinetIpMapper.queryByCabinetNo(cabinetNo, SettingConstant.IS_AVAILABLE);
        if (null == ips) {
            throw new IssuedException("10004", "机柜IP不存在");
        }

        var ctx = caffeinePut.getChannelContext(ips.getIp());
        if (null == ctx) {
            throw new IssuedException("10005", "设备连接失败");
        }

        var setType = settings.getSetType();
        var commandType = StepTypeNoEnum.STEP_TYPE_CCC_NUM.getMsg(setType);
        // 充放电下发指令数据
        var limit = issuedLimit(settings, step.getChargeCurrentLimit(), setType);
        // 终止或结束方案执行时间2秒钟
        var ts = NumConstant.INT_SUSPEND == settings.getSettingOrder()
                || NumConstant.INT_FINISH == settings.getSettingOrder()
                || settings.getExpectedTime() < NumConstant.INT_ZERO
                ? NumConstant.INT_ZERO : settings.getExpectedTime() * NumConstant.INT_SIXTY;
        // 中断继续执行时长不能为空
        if (expectedTimeSecond != null) {
            ts = expectedTimeSecond;
        }
        limit.setTimestamp(ts);

        List<String> oneLoadDataList = new ArrayList<>();
        for (var coi : commandOutInfos) {
            oneLoadDataList.add(getCommandLoadData(step, coi.getBoardNoNumber(), coi.getCellsNoNumber(), commandType, limit));
        }

        List<String> sendDataList = new ArrayList<>(getCommand(cabinetNo, oneLoadDataList));
        sendDataList = sendDataList.stream().filter(StringUtils::isNotEmpty).toList();
        if (CollectionUtils.isEmpty(sendDataList)) {
            throw new IssuedException("10006", "指令生成失败");
        }

        try {
            int i = 0;
            for (var sendData : sendDataList) {
                if (!SettingConstant.SHELVE.equalsIgnoreCase(setType)) {
                    // 对接需要16进制
                    ctx.writeAndFlush(Unpooled.buffer().writeBytes(HexUtil.hexString2Bytes(sendData)));

                    // 设置缓存
                    var cfKey = putCache(command, sendData, cabinetNo, i);

                    log.info("设备指令下发,缓存[{}]-[{}]send[{}]机柜[{}]", cfKey, setType, sendData, cabinetNo);
                    i++;
                }
                // 延时 20毫秒
                Thread.sleep(NumConstant.INT_TWO_TEN);
            }

            var cab = Integer.parseInt(reqCabinetNo);
            // 指令下发成功后,判断第一帧上送数据事件
            if (command.getStepId() > NumConstant.LONG_ZERO) {
                for (var coi : commandOutInfos) {
                    command.setExpectedTimeSecond(ts);
                    command.setSetType(settings.getSetType());
                    command.setInterruptOrEndStepId(step.getId());
                    commandFirstDataService.commandFirstData(new CommandFirstDataEvent(command, cab, coi.getBoardAddress(), coi.getCells()));
                }
            }
        } catch (Exception e) {
            log.error("控制指令下发,机柜[{}][{}]指令下发下发失败Charge and discharge control command issued [{}] ", cabinetNo, setType, sendDataList, e);
            throw new IssuedException("10007", "指令下发异常");
        }

        return true;
    }

    private String putCache(CommandIssuedSuccess commandIssued, String sendData, String cabinetNo, int i) {
        // 指令下发结果缓确认缓存
        var messageId = sendData.substring(sendData.length() - NumConstant.INT_EIGHT).substring(NumConstant.INT_ZERO, NumConstant.INT_FOUR);
        var cfKey = CaffeineUtil.COMMAND_CONFIRMATION + cabinetNo + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL + messageId;
        var cf = new CommandConfirmation();
        cf.setKey(cfKey);
        cf.setSuccess(false);
        cf.setMessageId(messageId);
        cf.setCabinetCode(cabinetNo);
        CaffeineUtil.set(cfKey, cf, 50);

        var idKey = CaffeineUtil.COMMAND_CONFIRMATION_ID
                + cabinetNo
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + commandIssued.getBoardNo()
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + commandIssued.getChannelNo()
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + commandIssued.getStepId()
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + i
                + CaffeineUtil.CACHE_KEY_CONNECTION_SYMBOL
                + messageId;
        CaffeineUtil.set(idKey, messageId, 50);

        return cfKey;
    }


    protected IssuedLimit issuedLimit(AddSettings settings, int chargeCurrentLimit, String setType) {
        var commandFlag = ChargeTypeUtil.isDischargeType(setType) ? StepTypeEnum.STEP_OPERA_DISCHARGE.getCode() : StepTypeEnum.STEP_OPERA_CHARGE.getCode();
        var limit = NumConstant.INT_ZERO;
        var voltage = NumConstant.INT_ZERO;
        var current = NumConstant.INT_ZERO;

        if (SettingConstant.CONSTANT_CURRENT_CONSTANT_VOLTAGE.equalsIgnoreCase(setType)) {
            voltage = settings.getVoltage();
            current = settings.getCurrent();
            limit = settings.getCutOff();
        }
        if (SettingConstant.CONSTANT_CURRENT.equalsIgnoreCase(setType)) {
            voltage = settings.getCutOff();
            current = settings.getCurrent();
            limit = settings.getCutOff();
        }
        if (SettingConstant.CONSTANT_VOLTAGE.equalsIgnoreCase(setType)) {
            voltage = settings.getVoltage();
            // 最大充电限制电流
            current = chargeCurrentLimit;
            limit = settings.getCutOff();
        }
        if (SettingConstant.CONSTANT_CURRENT_DISCHARGE.equalsIgnoreCase(setType)) {
            voltage = settings.getCutOff();
            current = settings.getCurrent();
            limit = settings.getCutOff();
        }

        // 恒功率放电
        if (SettingConstant.CONSTANT_POWER_DISCHARGE.equalsIgnoreCase(setType)) {
            voltage = settings.getVoltage();
            current = NumConstant.INT_ZERO;
            limit = settings.getCutOff();
        }

        // 恒功率充电
//        if (SettingConstant.CONSTANT_POWER_CHARGE.equalsIgnoreCase(setType)) {
//            voltage = settings.getVoltage();
//            current = NumConstant.INT_ZER;
//            limit = settings.getCutOff();
//        }

        // 恒阻放电
        if (SettingConstant.CONSTANT_RESISTANCE_DISCHARGE.equalsIgnoreCase(setType)) {
            voltage = settings.getVoltage();
            current = NumConstant.INT_ZERO;
            limit = settings.getCutOff();
        }

        return IssuedLimit.builder()
                .limit(limit)
                .current(current)
                .voltage(voltage)
                .commandFlag(commandFlag)
                .build();
    }

    private List<String> getCommand(String cabinetNo, List<String> oneLoadDataList) {
        if (StringUtil.isNumeric(cabinetNo) && StringUtil.toInt(cabinetNo) <= NumConstant.INT_LT_ZERO) {
            throw new IssuedException("50001", "机柜地址异常");
        }

        var address = StringUtil.isNumeric(cabinetNo) ? Integer.toHexString(StringUtil.toInt(cabinetNo)) : cabinetNo;

        List<String> strList = new ArrayList<>();
        var count = NumConstant.INT_ZERO;
        var loadData = new StringBuilder();
        for (var i = NumConstant.INT_ZERO; i < oneLoadDataList.size(); i++) {
            count++;
            loadData.append(oneLoadDataList.get(i));
            if (count == NumConstant.INT_FOUR) {
                var str = getString(address, loadData.toString());
                strList.add(str);
                loadData = new StringBuilder();
                count = NumConstant.INT_ZERO;
            }

            if (i == oneLoadDataList.size() - NumConstant.INT_ONE && count > NumConstant.INT_ZERO) {
                var str = getString(address, loadData.toString());
                strList.add(str);
            }
        }

        return strList;
    }

    protected String getString(String address, String loadData) {
        if (StringUtils.isAnyBlank(address, loadData)) {
            return null;
        }

        var issuedProtocol = new IssuedProtocol();
        // 待确认
        issuedProtocol.setDataType(ServerConstant.CMD_TYPE_CHARGE_DISCHARGE_CONTROL);
        issuedProtocol.setCabinetAddress(CabinetNoUtil.getCabinetNo(address));
        var data = loadData.trim().toUpperCase();

        issuedProtocol.setLoadData(data);
        var xorData = issuedProtocol.getHeader()
                + issuedProtocol.getCabinetAddress()
                + issuedProtocol.getDataType()
                + issuedProtocol.getDataLen()
                + data;
        var xor = XorDataUtils.getXor(xorData.getBytes(StandardCharsets.UTF_8));
        issuedProtocol.setXor(xor.trim());

        var len = (loadData.length() / NumConstant.INT_TWO) + NumConstant.INT_TWO;
        // 负载有效数据长度
        issuedProtocol.setDataLen(HexUtil.fillZero(String.format("%04x", len), NumConstant.INT_FOUR).toUpperCase());
        // 消息唯一标识
        var flagXorData = issuedProtocol.getHeader()
                + issuedProtocol.getCabinetAddress()
                + issuedProtocol.getDataType()
                + issuedProtocol.getDataLen()
                + HexUtil.str2HexStr(System.currentTimeMillis() + "");
        var flagXor = XorDataUtils.getXor(flagXorData.getBytes(StandardCharsets.UTF_8));
        var messageId = HexUtil.fillZero(flagXor, NumConstant.INT_FOUR);
        issuedProtocol.setMessageUniqueIdentifier(messageId);

        //log.info("下发指令messageId[{}],机柜[{}]", messageId, issuedProtocol.getCabinetAddress());
        return issuedProtocol.getAllData().replaceAll(" ", "");
    }

    protected String getCommandLoadData(BasicStepSettings step, Integer boardNum, Integer channelNum, String commandType, IssuedLimit issuedLimit) {

        var commandLoadData = new CommandLoadData();
        commandLoadData.setControlBoardAddress(boardNum);
        commandLoadData.setBatteryChannel(channelNum);
        commandLoadData.setCurrentCutoff(issuedLimit.getLimit());
        commandLoadData.setCommandType(Integer.parseInt(commandType));
        commandLoadData.setUpperCurrentLimit(step.getChargeCurrentLimit());
        commandLoadData.setUpperVoltageLimit(step.getChargeVoltageLimit());
        commandLoadData.setChargeDisTimeSeconds(issuedLimit.getTimestamp());
        commandLoadData.setVoltageUpperLowerLimits(issuedLimit.getVoltage());
        commandLoadData.setCurrentUpperLowerLimits(issuedLimit.getCurrent());
        commandLoadData.setLowerVoltageLimit(step.getDisChargeVoltageLimit());
        commandLoadData.setLowerCurrentLimit(step.getDisChargeCurrentLimit());

        return commandLoadData.getCommand().replaceAll(" ", "");
    }


    public String getOneCmd(CommandIssuedSuccess commandIssued, String reqCabinetNo, List<CommandOutInfo> commandOutInfos,
                            AddSettings settings, BasicStepSettings step, Integer expectedTimeSecond) {
        if (commandIssued == null || StringUtils.isAnyBlank(reqCabinetNo) || settings == null || step == null) {
            return null;
        }

        var cabinetNo = CabinetNoUtil.getCabinetNo(reqCabinetNo);
        var ips = cabinetIpMapper.queryByCabinetNo(cabinetNo, SettingConstant.IS_AVAILABLE);
        if (ips == null) {
            return null;
        }

        var setType = settings.getSetType();
        var commandType = StepTypeNoEnum.STEP_TYPE_CCC_NUM.getMsg(setType);
        // 充放电下发指令数据
        var limit = issuedLimit(settings, step.getChargeCurrentLimit(), setType);
        // 终止或结束方案执行时间2秒钟
        var ts = NumConstant.INT_SUSPEND == settings.getSettingOrder()
                || NumConstant.INT_FINISH == settings.getSettingOrder()
                || settings.getExpectedTime() < NumConstant.INT_ZERO
                ? NumConstant.INT_ZERO : settings.getExpectedTime() * NumConstant.INT_SIXTY;
        // 中断继续执行时长不能为空
        if (expectedTimeSecond != null) {
            ts = expectedTimeSecond;
        }
        limit.setTimestamp(ts);

        List<String> oneLoadDataList = new ArrayList<>();
        for (var coi : commandOutInfos) {
            oneLoadDataList.add(getCommandLoadData(step, coi.getBoardNoNumber(), coi.getCellsNoNumber(), commandType, limit));
        }

        return CollectionUtils.isEmpty(oneLoadDataList) ? null : oneLoadDataList.getFirst();
    }
}
